home *** CD-ROM | disk | FTP | other *** search
- ;;; -*- Mode:Lisp; Package:Weyli; Syntax:Common-Lisp; Base:10; Lowercase:T -*-
- ;;; ===========================================================================
- ;;; GF(p)
- ;;; ===========================================================================
- ;;; (c) Copyright 1989, 1991 Cornell University
-
- ;;; $Id: gfp.lisp,v 2.11 1991/10/24 19:19:36 rz Exp $
-
- (in-package "WEYLI")
-
- (define-domain-element-classes GFp GFp-element)
-
- (defmethod number-of-elements ((domain GFp))
- (characteristic domain))
-
- (defmethod number-of-elements ((domain GFq))
- (expt (characteristic domain) (field-degree domain)))
-
- (defmethod make-GFp-domain ((characteristic integer) (degree integer))
- (cond ((= degree 1)
- (make-instance 'gfp :characteristic characteristic))
- (t (error "Can't do GF(~D^~D) yet" characteristic degree)
- ;; This is where GFq domains are to be defined.
- )))
-
- (defmethod print-object ((d GFp) stream)
- #+Genera
- (format stream "~'bGF~(~D)" (characteristic d))
- #-Genera
- (format stream "GF(~D)" (characteristic d)))
-
- (defmethod make-element ((domain GFp) (value integer) &rest ignore)
- (declare (ignore ignore))
- (let ((modulus (characteristic domain)))
- (make-instance 'GFp-element
- :domain domain
- :value (reduce-modulo-integer value modulus))))
-
- ;; Could have more error checking
- (defmethod weyl::make-element ((domain GFp) (value integer) &rest ignore)
- (declare (ignore ignore))
- (make-element domain value))
-
- (defmethod print-object ((x GFp-element) stream)
- (with-slots (value domain) x
- (format stream "~D(~D)" value (characteristic domain))))
-
- (defmethod = ((x GFp-element) (y GFp-element))
- (with-slots ((v1 value) (d1 domain)) x
- (with-slots ((v2 value) (d2 domain)) y
- (and (eq d1 d2) (eql v1 v2)))))
-
- (defmethod 0? ((x GFp-element))
- (with-slots (value) x
- (lisp:zerop value)))
-
- (defmethod 1? ((x GFp-element))
- (with-slots (value) x
- (eql value 1)))
-
- ;; The following three methods make finite fields behave like quotient fields
-
- (defmethod make-quotient-element ((domain GFp) (a GFp-element) (b GFp-element))
- (unless (eql domain (domain-of a))
- (error "~S should be an element of ~S" a domain))
- (unless (eql domain (domain-of b))
- (error "~S should be an element of ~S" b domain))
- (with-slots ((v1 value)) a
- (with-slots ((v2 value)) b
- (with-slots (characteristic) domain
- (make-element domain
- (lisp:* v1 (compute-inverse v2 characteristic)))))))
-
- (defmethod numerator ((a GFp-element))
- a)
-
- (defmethod denominator ((a GFp-element))
- (make-element (domain-of a) 1))
-
- (defmethod minus ((x GFp-element))
- (with-slots (value domain) x
- (with-slots (characteristic) domain
- (if (eql 2 characteristic) x
- (make-element domain (lisp:- characteristic value))))))
-
- ;;; There is no such thing as a negative number in finite fields.
- (defmethod minus? ((x GFp-element))
- nil)
-
- (defmethod plus? ((x GFp-element))
- (not (0? x)))
-
- (defmethod-binary plus GFp-element (a b)
- (make-element domain (lisp:+ (gfp-value a) (gfp-value b))))
-
- (defmethod difference ((a GFp-element) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) a
- (with-slots ((v2 value) (d2 domain)) b
- (cond ((not (eql d1 d2))
- (error "~S and ~S are not from the same domain" a b))
- (t (make-element d1 (lisp:- v1 v2)))))))
-
- (defmethod times ((a GFp-element) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) a
- (with-slots ((v2 value) (d2 domain)) b
- (cond ((not (eql d1 d2))
- (error "~S and ~S are not from the same domain" a b))
- (t (make-element d1 (lisp:* v1 v2)))))))
-
- (defmethod plus ((a GFp-element) (b integer))
- (with-slots ((v1 value) (d1 domain)) a
- (make-element d1
- (lisp:+ v1 (reduce-modulo-integer b (characteristic d1))))))
-
- (defmethod plus ((a integer) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) b
- (make-element d1
- (lisp:+ (reduce-modulo-integer a (characteristic d1)) v1))))
-
- (defmethod difference ((a GFp-element) (b integer))
- (with-slots ((v1 value) (d1 domain)) a
- (make-element d1
- (lisp:- v1 (reduce-modulo-integer b (characteristic d1))))))
-
- (defmethod difference ((a integer) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) b
- (make-element d1
- (lisp:- (reduce-modulo-integer a (characteristic d1)) v1))))
-
- (defmethod times ((a GFp-element) (b integer))
- (with-slots ((v1 value) (d1 domain)) a
- (make-element d1
- (lisp:* v1 (reduce-modulo-integer b (characteristic d1))))))
-
- (defmethod times ((a integer) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) b
- (make-element d1
- (lisp:* (reduce-modulo-integer a (characteristic d1)) v1))))
-
- ;; Takes the inverse of an integer N mod P. Solve N*X + P*Y = 1. N
- ;; is guaranteed to be less than P, since in the case where P is a
- ;; fixnum, N is also assumed to be one.
-
- (defmethod recip ((x GFp-element))
- (with-slots (value domain) x
- (with-slots (characteristic) domain
- (make-element domain (reduce-modulo-integer
- (compute-inverse value characteristic)
- characteristic)))))
-
- (defun compute-inverse (value modulus)
- (let ((a1 modulus)
- (a2 (if (lisp:< value 0) (lisp:+ value modulus) value))
- (y1 0)
- (y2 1)
- q)
- (loop
- (if (eql a2 1) (return (values y2 y1)))
- (if (lisp:zerop a2)
- (error "Inverse of zero divisor -- ~d modulo ~d"
- value modulus))
- (setq q (truncate a1 a2))
- (psetq a1 a2 a2 (lisp:- a1 (lisp:* a2 q)))
- (psetq y1 y2 y2 (lisp:- y1 (lisp:* y2 q))))))
-
- (defmethod expt ((x GFp-element) (e integer))
- (with-slots (value domain) x
- (cond ((eql 1 value) x)
- ((lisp:minusp e)
- (error "Raising ~D to a negative power ~D" x e))
- (t (make-element domain
- (expt-modulo-integer value e (characteristic domain)))))))
-
- (defmethod quotient ((a GFp-element) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) a
- (with-slots ((v2 value) (d2 domain)) b
- (cond ((eq d1 d2)
- (with-slots (characteristic) d1
- (make-element d1
- (lisp:* v1 (compute-inverse v2 characteristic)))))
- (t (error "Taking the quotient of elements of ~
- different fields: ~S, ~S"
- a b))))))
-
- (defmethod remainder ((a GFp-element) (b GFp-element))
- (error "Computing the remainder of ~D by ~D"
- a b))
-
- (defmethod gcd ((a GFp-element) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) a
- (with-slots ((v2 value) (d2 domain)) b
- (cond ((eq d1 d2) (make-element d1 1))
- (t (error "Taking the GCD of elements of different fields: ~S, ~S"
- a b))))))
-
- (defmethod lcm ((a GFp-element) (b GFp-element))
- (with-slots ((v1 value) (d1 domain)) a
- (with-slots ((v2 value) (d2 domain)) b
- (cond ((eq d1 d2) (make-element d1 1))
- (t (error "Taking the LCM of elements of different fields: ~S, ~S"
- a b))))))
-
- (defmethod random ((domain GFp))
- (make-element domain (lisp:random (characteristic domain))))
-
- (defmethod multiplicative-order ((a GFp-element))
- (with-slots (value domain) a
- (with-slots ((p characteristic)) domain
- (cond ((not (eql 1 (lisp:gcd value p)))
- *positive-infinity*)
- ((let ((group-order (totient p)))
- (do ((factors (factor group-order)
- (rest factors))
- (order group-order))
- ((null factors)
- order)
- (do ((i 0 (lisp:1+ i)))
- ((lisp:= i (cdar factors)))
- (setq order (lisp:/ order (caar factors)))
- (when (not (eql 1 (expt-modulo-integer value order p)))
- (setq order (lisp:* order (caar factors)))
- (return t))))))))))
-
- ;; GF(2^n)
- (defvar *GF2-irreducible-polynomials*
- '(#O7 #O13 #O23 #O45 #O103 #O211 #O435 #O1021 #O2011 #O4005 #O10123
- #O20033 #O42103 #O100003 #O210013))
-
- (defmethod make-GFp-domain ((characteristic (eql 2)) (degree integer))
- (cond ((= degree 1)
- (make-instance 'gfp :characteristic characteristic))
- ((< degree (+ (length *GF2-irreducible-polynomials*) 2))
- (let* ((mask (ash 1 degree))
- (field (1- mask))
- (min-poly (logand (nth (- degree 2) *GF2-irreducible-polynomials*)
- field)))
- (make-instance 'GF2^n
- :degree degree
- :reduction-table
- (loop for i below degree
- for x^n = min-poly then (ash x^n 1)
- collect
- (if (lisp:zerop (logand mask x^n)) x^n
- (setq x^n (logxor (logand field x^n) min-poly))))
- :characteristic characteristic)))
- (t (error "Table doesn't go far enough: 2^~D" degree))))
-
- (defmethod print-object ((domain GF2^n) stream)
- #+Genera
- (format stream "~'bGF~(2^~D)" (field-degree domain))
- #-Genera
- (format stream "GF(2^~D)" (field-degree domain)))
-
- (defclass GF2^n-element (GFp-element)
- ())
-
- (defmethod print-object ((elt GF2^n-element) stream)
- (format stream "~V,'0B(2^~D)"
- (field-degree (domain-of elt)) (GFp-value elt)
- (field-degree (domain-of elt))))
-
- (defmethod make-element ((domain GF2^n) (value integer) &rest ignore)
- (declare (ignore ignore))
- (make-instance 'GF2^N-element
- :domain domain
- :value (logand (1- (ash 1 (field-degree domain))) value)))
-
- ;; Could have more error checking
- (defmethod weyl::make-element ((domain GF2^n) (value integer) &rest ignore)
- (declare (ignore ignore))
- (make-element domain value))
-
- (defmethod multiplicative-order ((a GF2^n-element))
- (let ((group-size (1- (number-of-elements (domain-of a)))))
- (loop for order in (all-divisors group-size)
- do (when (1? (expt a order))
- (return order)))))
-
- (defmethod-binary plus GF2^n-element (a b)
- (make-element domain (logxor (gfp-value a) (gfp-value b))))
-
- (defmethod-binary times GF2^n-element (a b)
- (let ((x (Gfp-value a))
- (y (GFp-value b))
- (degree (field-degree domain))
- (acc 0) answer)
- (loop while (not (lisp:zerop y)) do
- (when (not (lisp:zerop (lisp:logand 1 y)))
- (setq acc (lisp:logxor acc x)))
- (setq x (lisp:ash x 1))
- (setq y (lisp:ash y -1)))
- (setq answer (lisp:logand (lisp:1- (lisp:ash 1 degree)) acc))
- (loop for hi-bits = (lisp:ash acc (lisp:- degree))
- then (lisp:ash hi-bits -1)
- for poly in (GFp-reduction-table domain)
- while (not (lisp:zerop hi-bits))
- do (unless (lisp:zerop (lisp:logand 1 hi-bits))
- (setq answer (lisp:logxor answer poly))))
- (make-instance 'GF2^N-element :domain domain :value answer)))
-
- (defmethod expt ((base GF2^n-element) (expt integer))
- (%funcall (repeated-squaring #'times (make-element (domain-of base) 1))
- base expt))
-
- (defmethod recip ((x GF2^n-element))
- (let ((domain (domain-of x)))
- (expt x (lisp:- (lisp:expt 2 (field-degree domain)) 2))))
-
- (defmethod-binary quotient GF2^n-element (x y)
- (* x (recip y)))
-
- ;; GF(m)
-
- ;; This domain is the union of all Z/mZ for all m.
-
- (define-domain-element-classes GFm GFm-element)
-
- (defmethod make-element ((domain GFm) value &rest rest)
- (let ((modulus (first rest)))
- (make-instance 'GFm-element
- :domain domain
- :value (reduce-modulo-integer value modulus)
- :modulus modulus)))
-
- ;; Could have more error checking
- (defmethod weyl::make-element ((domain GFm) value &rest rest)
- (%apply #'make-element domain value rest))
-
- (defmethod print-object ((x GFm-element) stream)
- (with-slots (value modulus) x
- (format stream "~D(~D)" value modulus)))
-
- (defmethod = ((x GFm-element) (y GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) x
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) y
- (and (eq d1 d2) (eql v1 v2) (eql m1 m2)))))
-
- (defmethod 0? ((x GFm-element))
- (with-slots (value) x
- (lisp:zerop value)))
-
- (defmethod 1? ((x GFm-element))
- (with-slots (value) x
- (eql value 1)))
-
- (defmethod minus ((x GFm-element))
- (with-slots (value modulus domain) x
- (if (eql 2 modulus) x
- (make-element domain (lisp:- modulus value) modulus))))
-
- ;;; There is no such thing as a negative number in finite fields.
- (defmethod minus? ((x GFm-element))
- nil)
-
- (defmethod plus? ((x GFm-element))
- (not (0? x)))
-
- (defmethod plus ((a GFm-element) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) b
- (cond ((not (eql d1 d2))
- (error "~S and ~S are not from the same domain" a b))
- ((eql m1 m2)
- (make-element d1 (lisp:+ v1 v2) m1))
- (t (make-element d1 (lisp:+ v1 v2) (lisp:gcd m1 m2)))))))
-
- (defmethod difference ((a GFm-element) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) b
- (cond ((not (eql d1 d2))
- (error "~S and ~S are not from the same domain" a b))
- ((eql m1 m2)
- (make-element d1 (lisp:- v1 v2) m1))
- (t (make-element d1 (lisp:- v1 v2) (lisp:gcd m1 m2)))))))
-
- (defmethod times ((a GFm-element) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) b
- (cond ((not (eql d1 d2))
- (error "~S and ~S are not from the same domain" a b))
- ((eql m1 m2)
- (make-element d1 (lisp:* v1 v2) m1))
- (t (make-element d1 (lisp:* v1 v2) (lisp:gcd m1 m2)))))))
-
- (defmethod plus ((a GFm-element) (b integer))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (make-element d1 (lisp:+ v1 (reduce-modulo-integer b m1)) m1)))
-
- (defmethod plus ((a integer) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) b
- (make-element d1 (lisp:+ (reduce-modulo-integer a m1) v1) m1)))
-
- (defmethod difference ((a GFm-element) (b integer))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (make-element d1 (lisp:- v1 (reduce-modulo-integer b m1)) m1)))
-
- (defmethod difference ((a integer) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) b
- (make-element d1 (lisp:- (reduce-modulo-integer a m1) v1) m1)))
-
- (defmethod times ((a GFm-element) (b integer))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (make-element d1 (lisp:* v1 (reduce-modulo-integer b m1)) m1)))
-
- (defmethod times ((a integer) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) b
- (make-element d1 (lisp:* (reduce-modulo-integer a m1) v1) m1)))
-
- ;;; Takes the inverse of an integer N mod P. Solve N*X + P*Y = 1. N
- ;;; is guaranteed to be less than P, since in the case where P is a
- ;;; fixnum, N is also assumed to be one.
-
- (defmethod recip ((x GFm-element))
- (with-slots (value modulus domain) x
- (make-element domain (reduce-modulo-integer (compute-inverse value modulus)
- modulus)
- modulus)))
-
- (defmethod expt ((x GFm-element) (e integer))
- (with-slots (value modulus domain) x
- (cond ((eql 1 value) x)
- ((lisp:minusp e)
- (error "Raising ~D to a negative power ~D" x e))
- (t (make-element domain (expt-modulo-integer value e modulus)
- modulus)))))
-
- (defmethod quotient ((a GFm-element) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) b
- (make-element d1 (lisp:* v1 (compute-inverse v2 m2)) m1))))
-
- (defmethod remainder ((a GFm-element) (b GFm-element))
- (error "Computing the remainder of ~D by ~D"
- a b))
-
- (defmethod gcd ((a GFm-element) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) b
- (make-element d1 1 m1))))
-
- (defmethod lcm ((a GFm-element) (b GFm-element))
- (with-slots ((v1 value) (m1 modulus) (d1 domain)) a
- (with-slots ((v2 value) (m2 modulus) (d2 domain)) b
- (make-element d1 1 m1))))
-
- (defmethod multiplicative-order ((a GFm-element))
- (with-slots (value modulus domain) a
- (cond ((not (eql 1 (lisp:gcd value modulus)))
- *positive-infinity*)
- ((let ((group-order (totient modulus)))
- (do ((factors (factor group-order)
- (rest factors))
- (order group-order))
- ((null factors)
- order)
- (do ((i 0 (lisp:1+ i)))
- ((lisp:= i (cdar factors)))
- (setq order (lisp:/ order (caar factors)))
- (when (not (eql 1 (expt-modulo-integer value order modulus)))
- (setq order (lisp:* order (caar factors)))
- (return t)))))))))
-
- (defmethod print-object ((d simple-finite-field) stream)
- #+Genera
- (format stream "~'bGF~(~D)" (characteristic d))
- #-Genera
- (format stream "GF(~D)" (characteristic d)))
-
- ;; These are the guys that actually create the finite fields.
- (defun make-finite-field* (size)
- (cond ((null size)
- (make-instance 'gfm))
- ((prime? size)
- (make-GFp-domain size 1))
- (t (let* ((s (factor size))
- (char (first (first s)))
- (degree (rest (first s))))
- (if (null (rest s))
- (make-Gfp-domain char degree)
- (error "Finite fields of size ~S=~S don't exist" size s))))))
-
-
- (defun make-finite-field (&optional size)
- (add-domain #'false (make-finite-field* size)))
-
- ;; This is slightly inefficient, but who cares... I want to localize
- ;; the knowledge of how to create domains in the MAKE-...* functions.
- (defun get-finite-field (&optional size)
- (cond ((null size)
- (add-domain (lambda (d) (eql (class-name (class-of d)) 'GFm))
- (make-finite-field* size)))
- ((prime? size)
- (add-domain (lambda (d)
- (and (eql (class-name (class-of d)) 'GFp)
- (eql (ring-characteristic d) size)))
- (make-finite-field* size)))
- ((null (rest (factor size)))
- (add-domain (lambda (d)
- (and (eql (class-name (class-of d)) 'GF2^n)
- (eql (lisp:expt (ring-characteristic d)
- (field-degree d))
- size)))
- (make-finite-field* size)))
- (t (error "Can't do algebraic extensions yet"))))
-